package upm.pb4agent.team2.environment;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec.CodecException;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.SimpleBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.util.leap.ArrayList;
import jade.util.leap.HashMap;
import jade.util.leap.Iterator;
import jade.util.leap.List;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.Vector;

import upm.pb4agent.ontology.Dock;
import upm.pb4agent.ontology.DockDesignation;
import upm.pb4agent.ontology.Registration;
import upm.pb4agent.ontology.RegistrationForm;
import upm.pb4agent.team2.Constants;
import upm.pb4agent.team2.Team2BaseAgent;
import upm.pb4agent.team2.internalOntology.StartRacePlease;
import upm.pb4agent.team2.internalOntology.TeamId;
import upm.pb4agent.team2.internalOntology.TeamRacing;

public class RegistrationDesk extends Team2BaseAgent 
{

	// -------------------------------------------
	// -------------- PROPERTIES -----------------
	// -------------------------------------------

	private static final long serialVersionUID = 3571086341566215858L;
	
	private boolean isRegistrationClosed = false;
	private Long registrationLength;
	private Long registrationStartTime;
	

	private Vector<AID> registeredTeamsManagerAIDs = new Vector<AID>();
	
	// -------------------------------------------
	// --------------- METHODS -------------------
	// -------------------------------------------

	protected void setup() 
	{
		super.setup();
		
		registerOnDF(Constants.REGISTRATION_DESK);
		
		try 
		{
			registrationLength = Long.valueOf(getPropertyFromConfigFile("registration_period"));
		} 
		catch (NumberFormatException e) 
		{
			e.printStackTrace();
		} 
		catch (FileNotFoundException e) 
		{
			e.printStackTrace();
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
		
		registrationStartTime = new Date().getTime();

		addBehaviour(new ProcessRegistration(this));
	}

	// -------------------------------------------
	// --------------- CLASSES -------------------
	// -------------------------------------------

	class ProcessRegistration extends SimpleBehaviour 
	{

		// Variable declaration

		private static final long serialVersionUID = -4988299832688074506L;
		
		private TeamRacing teamRacing;

		public ProcessRegistration(Agent agent) {
			super(agent);
		}

		@Override
		public void action() 
		{
			// Variable declaration
			Long currentTime = new Date().getTime();
			
			boolean isRegistrationFormValid = true;

			ACLMessage aclMessage = receive(MessageTemplate.and(MessageTemplate.MatchLanguage(codec.getName()), MessageTemplate.MatchOntology(ontology.getName())));

			printReceivedProtocol(aclMessage);
			
			if (aclMessage != null) 
			{
				try 
				{
					ContentElement contentElement = null;
					ACLMessage reply = aclMessage.createReply();

					// Wait for registration requests
					if (aclMessage.getPerformative() == ACLMessage.REQUEST) 
					{
						// Process request
						// Check if registration is closed
						if (!isRegistrationClosed) 
						{
							// Registration is still open
							try 
							{
								contentElement = getContentManager().extractContent(aclMessage);

								if (contentElement instanceof Action) 
								{
									Action action = (Action) contentElement;
									Concept concept = action.getAction();

									if (concept instanceof Registration) 
									{
										RegistrationForm registrationForm = ((Registration) concept).getRegistrationForm();

										teamRacing = new TeamRacing();
										
										// Validate the registration form
										isRegistrationFormValid = validateRegistrationForm(registrationForm
												.getDockDesignation());

										if (isRegistrationFormValid == true) 
										{
											if(!(registeredTeamsManagerAIDs.contains(aclMessage.getSender())))
											{
												// Create the team's watcher
												String watcherName = "Watcher" + registrationForm.getTeamID().getValue();
												AID watcherAID = createAndStartAgent(watcherName, "upm.pb4agent.team2.environment.Watcher", new String[] {  });
	
												// Inform the sender
												reply.setPerformative(ACLMessage.INFORM);
												myAgent.send(reply);
												
												TeamId teamId = new TeamId();
												teamId.setValue(registrationForm.getTeamID().getValue());
												teamRacing.setTeamID(teamId);
												
												// Send teamRacing to the watcher...
												sendInternalMessage(watcherAID, ACLMessage.INFORM, new Action(watcherAID, teamRacing), TeamRacing.class.getSimpleName().toString());
												
												registeredTeamsManagerAIDs.addElement(aclMessage.getSender());
											}
											else
											{
												printOut(aclMessage.getSender().getLocalName()+" attempted to register it's team again - team already registered.");
											}
										}
									}
								}
							} 
							catch (CodecException e) 
							{
								e.printStackTrace();
							}
						}

						if (isRegistrationClosed) 
						{
							
							// Refuse the request
							refuseRegistration(reply, "registration period is over");
							
						}
						if (!isRegistrationFormValid)
						{
							refuseRegistration(reply, "registration form is invalid");
						}
					} else {

						// Message format is wrong
						reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
						myAgent.send(reply);
					}
				} 
				catch (OntologyException oe) 
				{
					oe.printStackTrace();
				}
			}
			else 
			{
				if (currentTime - registrationStartTime >= registrationLength) 
				{
					printOut("Registration period has closed.");
					
					// Inform the referee the to start the race
					AID refereeAID = findAgentOnDF(myAgent, Constants.REFEREE);
					
					//Better use an ontology - more formal
					//sendStringMessage(refereeAID, ACLMessage.INFORM, Constants.START_RACE_PLEASE);
					
					StartRacePlease startRacePlease = new StartRacePlease();
									startRacePlease.setNumberOfBoatsRacing(registeredTeamsManagerAIDs.size());
									
					sendInternalMessage(refereeAID, ACLMessage.INFORM, new Action(refereeAID, startRacePlease), StartRacePlease.class.getSimpleName().toString());
					
					isRegistrationClosed = true;
				} 
			}
		}
		
		private void refuseRegistration(ACLMessage reply, String reason)
		{
			// Refuse the request
			reply.setPerformative(ACLMessage.REFUSE);
			myAgent.send(reply);
			
			System.out.println(getLocalName() + ": Refusing registration because " + reason);
		}

		@Override
		public boolean done() 
		{
			return isRegistrationClosed;
		}

		private boolean validateRegistrationForm(List dockDesignationList)
		{
			boolean isValid = true;
			List rowerIDList = new ArrayList();
			List dockNameList = new ArrayList();
			AID rowerID = null;
			Dock dock = null;
			Iterator iterator = dockDesignationList.iterator();

			while (iterator.hasNext() == true) 
			{
				DockDesignation dockDesignation = (DockDesignation) iterator.next();
				rowerID = dockDesignation.getRowerID();
				dock = dockDesignation.getDock();

				// Check if the ID is already in the list
				if (rowerIDList.contains(rowerID) == true) 
				{
					isValid = false;
					break;
				} 
				else 
				{
					rowerIDList.add(rowerID);
					teamRacing.addRowerAIDs(rowerID);
				}

				// Check if the dock is already in the list
				if (dockNameList.contains(dock.getName()) == true) 
				{
					isValid = false;
					break;
				} 
				else 
				{
					dockNameList.add(dock.getName());
				}
			}
			return isValid;
		}
	}
}
